/*
 *  $Id: Card.cs 1199 2010-11-20 17:38:24Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

using AgateLib.DisplayLib;
using AgateLib.Geometry;

namespace Ragima {
    /// <summary>
    /// Степень завершенности карты
    /// </summary>
    public enum CompleteLevel {
        Unknown,
        TODO,
        InProcess,
        ReadyForTest,
        Final,
    }

    /// <summary>
    /// Тип карт
    /// </summary>
    public enum CardType {
        Unknown,
        Creature,
        Land,
        Spell,
        Enchantment,
        Ability,
        PlayerAbility,
    }

    /// <summary>
    /// Местонахождение карт
    /// </summary>
    public enum CardPlace {
        None,
        Hands,
        Deck,
        Deck2,
        Grave,
    }

    /// <summary>
    /// Абстрактная карта
    /// Содержит: идентификатор, название, имена файлов картинок, стихия, ценность, условия
    /// </summary>
    public abstract class Card: Languages.INamed, IHelpProvider, IHintable, IDescriptionSource {
        public const string CardPrefix = "card.";

        public CompleteLevel Level;
        public readonly string Id;
        public int Version;
        private Languages.Names _name;
        private ElementType _element, _additionalElement;
        private Languages.Phrase _description;
        public Languages.Phrase LogMessage;
        public int Value;
        public int MaxCount;
        public CardCategory Category;
        public bool AllowInOffence;
        public bool AllowInDefence;
        private ElementValues _manas;
        private List<Condition> _conditions;
        private TargetType _target;
        private Surface _smallPic, _bigPic;
        private Type _picCardType;
        private Card _picCard;
        private int _weight;
        private CardDescription _drawDesc;

        public Card(string id) {
            Id = id;
            _name = new Languages.Names(GetResourceId("name"), Animate);
            _description = GetNewPhrase("desc");
            LogMessage = null;
            Level = CompleteLevel.Unknown;
            Version = 0;
            Element = ElementType.Neutral;
            MaxCount = DefaultMaxCount;
            Category = CardCategory.None;
            AllowInOffence = DefaultAllowInOffence;
            AllowInDefence = DefaultAllowInDefence;
            _manas = new ElementValues();
            _conditions = new List<Condition>();
            _weight = int.MinValue;
        }

        protected virtual bool Animate {
            get { return false; }
        }

        public string GetResourceId(string suffix) {
            return CardPrefix+Id+"."+suffix;
        }
        public Languages.Phrase GetNewPhrase(string suffix) {
            return new Languages.Phrase(GetResourceId(suffix));
        }
        public Languages.Phrase GetTargetPhrase() {
            return GetNewPhrase("target");
        }
        public Languages.Phrase GetConditionPhrase() {
            return GetNewPhrase("condition");
        }
        public Languages.Phrase GetLogPhrase() {
            return GetNewPhrase("log");
        }

        public virtual CardType CardType {
            get { return CardType.Unknown; }
        }

        public bool IsSpell {
            get { return CardType == CardType.Spell || CardType == CardType.Enchantment; }
        }
        public bool IsAbility {
            get { return CardType == CardType.Ability || CardType == CardType.PlayerAbility; }
        }

        public virtual Languages.Names Name {
            get { return _name; }
        }

        public string CardName {
            get {
                string name = Name.ToString();
                if (string.IsNullOrEmpty(name))
                    return string.Empty;
                else
                    return char.ToUpper(name[0]) + name.Substring(1);
            }
        }

        private FilePath GetBigPictureName() { return Paths.CardImages[Id + ".jpg"]; }
        private FilePath GetSmallPictureName() { return Paths.CardImages[Id + "_s.jpg"]; }

        public virtual Surface BigPicture {
            get {
                GetPictureSource();
                if (_picCard != null) return _picCard.BigPicture;
                if (_bigPic == null) _bigPic = new Surface(GetBigPictureName());
                return _bigPic;
            }
        }
        public virtual Surface SmallPicture {
            get {
                GetPictureSource();
                if (_picCard != null) return _picCard.SmallPicture;
                if (_smallPic == null) _smallPic = new Surface(GetSmallPictureName());
                return _smallPic;
            }
        }

        protected void SetPictureSource<T>() where T : Card {
            _picCardType = typeof(T);
        }
        protected void SetPictureSource(Card card) {
            _picCardType = null;
            _picCard = card;
        }
        private void GetPictureSource() {
            if (_picCardType != null && _picCard == null) {
                _picCard = CardManager.GetCard(_picCardType);
                if (_picCard == this) _picCard = null;
                if (_picCard == null) _picCardType = null;
            }
        }

        public ElementType Element {
            get { return _element; }
            set {
                _element = value;
                _additionalElement = value;
            }
        }

        public ElementType AdditionalElement {
            get { return _additionalElement; }
            set { _additionalElement = value; }
        }

        /// <value>
        /// Описание карты
        /// </value>
        public virtual Languages.Phrase Description {
            get { return _description; }
        }

        /// <value>
        /// Описание карты в виде строки
        /// </value>
        public virtual string DescriptionText {
            get { return Description.GetText(); }
        }

        /// <value>
        /// Имеет ли карта описание
        /// </value>
        public bool HasDescription {
            get { return !string.IsNullOrEmpty(DescriptionText); }
        }

        /// <value>
        /// Значение максимального числа карт по умолчанию
        /// </value>
        public virtual int DefaultMaxCount {
            get { return 3; }
        }

        /// <value>
        /// Значение доступности карты в атаке по умолчанию
        /// </value>
        public virtual bool DefaultAllowInOffence {
            get { return true; }
        }

        /// <value>
        /// Значение доступности карты в защите по умолчанию
        /// </value>
        public virtual bool DefaultAllowInDefence {
            get { return false; }
        }

        /// <value>
        /// Вес карты
        /// </value>
        public int Weight {
            get {
                if (_weight == int.MinValue)
                    _weight = GetWeight();
                return _weight;
            }
        }

        /// <summary>
        /// Запрос веса карты
        /// </summary>
        protected virtual int GetWeight() {
            int weight = 0;
            switch (Element.GetRelationTo(AdditionalElement)) {
                case ElementRelation.Enemy:
                    weight += WeightCoeff.AdditionalEnemyElement;
                    break;
                case ElementRelation.Other:
                    weight += WeightCoeff.AdditionalElement;
                    break;
            }
            weight += (MaxCount - DefaultMaxCount) * WeightCoeff.CardMaxCount;
            if (AllowInOffence != DefaultAllowInOffence)
                weight += (AllowInOffence ? 1 : -1) * WeightCoeff.AllowOffenceCast;
            if (AllowInDefence != DefaultAllowInDefence)
                weight += (AllowInDefence ? 1 : -1) * WeightCoeff.AllowDefenceCast;
            weight += _manas[ElementType.Health] * WeightCoeff.HealthCost.GetWeight(ElementType.Health.GetRelationTo(Element));
            foreach (ElementType element in ElementType.ManaElements)
                weight += _manas[element] + WeightCoeff.ManaCost.GetWeight(element.GetRelationTo(Element));
            foreach (Condition condition in GetConditions())
                weight += condition.GetWeight(this);
            return weight;
        }

        /// <summary>
        /// Запрос здоровья карты
        /// </summary>
        public virtual int GetHealth() { return 0; }
        /// <summary>
        /// Запрос урона карты
        /// </summary>
        public virtual int GetDamage() { return 0; }

        /// <summary>
        /// Запрос цели карты
        /// </summary>
        public TargetType Target {
            get { return _target; }
            protected set { _target = value; }
        }

        /// <summary>
        /// Проверка условий карты для указанного игрока
        /// </summary>
        public bool TestConditionsFor(AbstractObject caster, bool notice) {
            // проверка фазы игры
            switch (caster.Owner.State) {
                case PlayerState.Offence:
                    if (!AllowInOffence) {
                        if (notice)
                            Game.Current.ErrorNotice(Resources.Game.CardNoOffence.GetText());
                        return false;
                    }
                    break;
                case PlayerState.Defence:
                    if (!AllowInDefence) {
                        if (notice)
                            Game.Current.ErrorNotice(Resources.Game.CardNoDefence.GetText());
                        return false;
                    }
                    break;
            }
            // проверка условий карты
            if (!Condition.TestAll(_conditions, caster, this, notice))
                return false;
            // проверка маны
            if (!caster.Owner.TestMana(GetManaCostFor(caster.Owner), notice))
                return false;
            // проверка блокировок карт
            if (caster.Owner.GlobalSummary.IsLockCard(caster.Owner, this, notice)) return false;
            // проверка цели
            if (Target == null || !Target.HasTargets(caster)) {
                if (notice) Game.Current.ErrorNotice(Resources.Game.CardNoTargets.GetText());
                return false;
            }
            // все проверки прошли успешно
            return true;
        }

        /// <summary>
        /// Действия по условиям (уменьшение ман, и т.д.)
        /// </summary>
        public void DoConditionsFor(TargetArgs args) {
            // обработка условий карты
            Condition.DoConditionAll(_conditions, args.Caster, this);
            // обработка маны
            ElementValues manas = GetManaCostFor(args);
            foreach (ElementType element in ElementType.AllElements)
                if (manas[element] > 0)
                    args.Player.DecrementMana(element, manas[element]);
        }

        /// <summary>
        /// Добавить условие в карту
        /// </summary>
        /// <param name="condition">Условие</param>
        protected void AddCondition(Condition condition) {
            _conditions.Add(condition);
        }

        /// <summary>
        /// Добавить цену карты в мане основной стихии
        /// </summary>
        /// <param name="value">Значение</param>
        protected void AddManaCost(int value) {
            _manas.Increment(Element, value);
        }

        /// <summary>
        /// Добавить цену карты в мане
        /// </summary>
        /// <param name="element">Стихия</param>
        /// <param name="value">Значение</param>
        protected void AddManaCost(ElementType element, int value) {
            _manas.Increment(element, value);
        }

        /// <value>
        /// Имеется ли хоть одно условие
        /// </value>
        public bool HasConditions {
            get { return _conditions.Count > 0 || !_manas.Empty; }
        }

        /// <summary>
        /// Получить список условий
        /// </summary>
        public IEnumerable<Condition> GetConditions() {
            return _conditions;
        }

        public ElementValues ManaCost {
            get { return _manas; }
        }

        public ElementValues GetManaCostFor(TargetArgs args) {
            ElementValues manas = GetManaCostFor(args.Player);
            ManaCostAction(args, manas);
            return manas;
        }

        public ElementValues GetManaCostFor(Player player) {
            ElementValues manas = _manas.Clone();
            if (player.GlobalSummary.ManaChangers.Count > 0) {
                foreach (IManaCostChanger changer in player.GlobalSummary.ManaChangers)
                    if (changer.Accept(player, this, manas))
                        changer.Change(player, this, manas);
            }
            return manas;
        }

        /// <summary>
        /// Получить суммарную истинную цену карты в мане
        /// </summary>
        /// <returns>Суммарная цена</returns>
        public int GetSumMana() {
            return _manas.Sum;
        }

        /// <summary>
        /// Получить цены карты в мане и прибавить их к массиву sum
        /// </summary>
        /// <param name="sum">Массив-сумматор цен/param>
        public void AddSumMana(ElementValues sum) {
            sum.Increment(_manas);
        }

        /// <summary>
        /// Получить суммарную реальную цену карты в мане для указанного игрока
        /// </summary>
        /// <returns>Суммарная цена</returns>
        public int GetSumManaFor(Player player) {
            ElementValues manas = GetManaCostFor(player);
            return manas.Sum;
        }

        public virtual void ManaCostAction(TargetArgs args, ElementValues manas) {
        }

        /// <summary>
        /// Проверка возможности действия перед его выполнением
        /// </summary>
        /// <param name="args">Параметры цели</param>
        /// <returns>true, если действие возможно</returns>
        public virtual bool IsPossibleAction(TargetArgs args) {
            return true;
        }

        /// <summary>
        /// Добавление сообщения о действии карты в лог
        /// </summary>
        /// <param name="log">Лог, в который добавлять</param>
        /// <param name="args">Параметры цели</param>
        public virtual void LogAction(Log log, TargetArgs args) {
            if (LogMessage != null)
                log.Add(LogMessage, args.Caster, this);
        }

        /// <summary>
        /// Действие при использовании карты
        /// </summary>
        /// <param name="player">Игрок, использующий карту</param>
        /// <param name="args">Аргументы цели использования</param>
        public virtual void DoAction(TargetArgs args) { }

        /// <value>
        /// Куда убирать карту после использования
        /// </value>
        public virtual CardPlace PlaceAfterUse { get { return CardPlace.Deck2; } }
        /// <value>
        /// Куда убирать карту после смерти
        /// </value>
        public virtual CardPlace PlaceAfterDeath { get { return CardPlace.Grave; } }

        /// <summary>
        /// Отрисовка карты в большом размере
        /// </summary>
        public void DrawBig(int x, int y) {
            if (BigPicture != null) {
                BigPicture.Draw(x, y);
            }
        }
        /// <summary>
        /// Отрисовка карты в маленьком размере
        /// </summary>
        public void DrawSmall(int x, int y) {
            if (SmallPicture != null) {
                SmallPicture.Draw(x, y);
            } else if (BigPicture != null) {
                BigPicture.Draw(new Rectangle(x, y, Constants.SmallCardWidth, Constants.SmallCardHeight));
            }
        }
        /// <summary>
        /// Отрисовка карты в маленьком размере с указанной прозрачностью
        /// </summary>
        public void DrawSmall(int x, int y, byte alpha) {
            double dAlpha = alpha/255.0;
            SmallPicture.Alpha = dAlpha;
            BigPicture.Alpha = dAlpha;
            DrawSmall(x, y);
            SmallPicture.Alpha = 1.0;
            BigPicture.Alpha = 1.0;
        }

        /// <summary>
        /// Отрисовка карты с рамкой цвета стихии
        /// </summary>
        public void DrawWithFrame(int x, int y) {
            DrawBig(x + 16, y + 25);
            if (Element == AdditionalElement) {
                Element.Frame.Draw(x, y);
            } else {
                Rectangle rect = new Rectangle(x, y, Constants.BigCardFrameWidth, Constants.BigCardFramePartHeight);
                Element.Frame.Draw(Constants.BigCardFrameUpperPart, rect);
                rect.Y += Constants.BigCardFramePartHeight;
                AdditionalElement.Frame.Draw(Constants.BigCardFrameLowerPart, rect);
            }
        }

        /// <summary>
        /// Отрисовка карты с рамкой цвета стихии и информацией о карте
        /// </summary>
        public virtual bool DrawHelp(int x, int y) {
            DrawWithFrame(x, y);
            DrawManas(x+20, y+294);
            Data.Fonts.CardName.Color = Color.Black;
            Data.Fonts.CardName.DrawCenter(x+150, y+4, CardName);
            DrawDescription(x+20, y+314);
            return true;
        }
        public void DrawManas(int x, int y) {
            string s;
            Data.Fonts.PlayerMana.Color = Color.Black;
            foreach (ElementType element in ElementType.AllElements) {
                if (_manas[element] > 0) {
                    element.ManaSymbol.Draw(x, y);
                    x += 20;
                    s = _manas[element].ToString();
                    Data.Fonts.PlayerMana.Draw(x, y + 2, s);
                    x += Data.Fonts.PlayerMana.GetWidth(s) + 4;
                }
            }
        }
        public void DrawManasRight(int x, int y) {
            DrawManas(x - GetManasWidth(), y);
        }
        private int GetManasWidth() {
            int width = 0;
            foreach (ElementType element in ElementType.AllElements) {
                if (_manas[element] > 0) {
                    width += 20 + Data.Fonts.PlayerMana.GetWidth(_manas[element].ToString()) + 4;
                }
            }
            return width;
        }
        private void DrawDescription(int x, int y) {
            if (_drawDesc == null)
                _drawDesc = new CardDescription(this, Constants.HelpDescriptionWidth);
            _drawDesc.Draw(x, y);
        }

        #region IHintable implementation
        public virtual Hint GetHint(Rectangle hintRect) {
            return new CardHint(hintRect, this);
        }
        #endregion
    }

    public class CardHint: Hint {
        private const int TextBlockWidth = 260;

        private Card _card;
        private List<string> _text;

        public CardHint(Rectangle hintRect, Card card): base(hintRect, new Size(Constants.SmallCardWidth+14+TextBlockWidth, Constants.SmallCardHeight+10)) {
            _card = card;
            _text = new List<string>();
            _text.AddRange(Data.Fonts.CardDescription.Wrap(card.CardName, TextBlockWidth));
            _text.AddRange(Data.Fonts.CardDescription.Wrap(card.DescriptionText, TextBlockWidth));
            BackgroundColor = card.Element.ElementColor;
        }

        protected override void DrawContent(Rectangle rect) {
            int x = rect.X + 4, y = rect.Y + 4;
            _card.DrawSmall(x, y);
            x += Constants.SmallCardWidth + 6;
            Data.Fonts.CardDescription.Color = Color.Black;
            for (int i = 0; i < _text.Count; i++) {
                Data.Fonts.CardDescription.Draw(x, y, _text[i]);
                y += 16;
            }
            _card.DrawManasRight(rect.Right - 4, rect.Bottom - 4 - 16);
        }
    }
}
